package com.joysuch.wwyt.licence.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.joysuch.wwyt.bp.entity.BpEnterpriseInfo;
import com.joysuch.wwyt.bp.service.BpEnterpriseInfoService;
import com.joysuch.wwyt.common.entity.BaseEnterpriseEntity;
import com.joysuch.wwyt.common.entity.BaseTenantEntity;
import com.joysuch.wwyt.config.RedisClient;
import com.joysuch.wwyt.core.entity.BaseConfig;
import com.joysuch.wwyt.core.entity.BaseMenuFunction;
import com.joysuch.wwyt.core.entity.BaseModuleMenuMapping;
import com.joysuch.wwyt.core.enums.BaseConfigCodes;
import com.joysuch.wwyt.core.enums.DeleteFlag;
import com.joysuch.wwyt.core.security.ShiroUser;
import com.joysuch.wwyt.core.service.AuthorityService;
import com.joysuch.wwyt.core.service.BaseConfigService;
import com.joysuch.wwyt.core.service.BaseModuleMenuMappingService;
import com.joysuch.wwyt.core.support.Context;
import com.joysuch.wwyt.licence.bean.LicenceAnalysisDto;
import com.joysuch.wwyt.licence.bean.LicenceCacheDto;
import com.joysuch.wwyt.licence.bean.LicenceFilePermissionDto;
import com.joysuch.wwyt.licence.bean.LicenceModuleAuthDto;
import com.joysuch.wwyt.licence.bean.LicenceModuleAuthPageDto;
import com.joysuch.wwyt.licence.bean.LicenceModuleAuthRemindDto;
import com.joysuch.wwyt.licence.bean.LicencePageDto;
import com.joysuch.wwyt.licence.constant.LicenceStateEnum;
import com.joysuch.wwyt.licence.entity.Licence;
import com.joysuch.wwyt.licence.entity.LicenceModuleAuth;
import com.joysuch.wwyt.licence.exception.LicenceException;
import com.joysuch.wwyt.licence.mapper.LicenceMapper;
import com.joysuch.wwyt.licence.service.LicenceModuleAuthService;
import com.joysuch.wwyt.licence.service.LicenceService;
import com.joysuch.wwyt.util.AESUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * licence表 服务实现类
 * </p>
 *
 * @author jialei
 * @since 2022-04-26
 */
@Service
public class LicenceServiceImpl extends ServiceImpl<LicenceMapper, Licence> implements LicenceService {

    private static final Logger LOGGER = LoggerFactory.getLogger(LicenceServiceImpl.class);

    private final BpEnterpriseInfoService bpEnterpriseInfoService;

    private final LicenceModuleAuthService licenceModuleAuthService;

    private final BaseModuleMenuMappingService baseModuleMenuMappingService;

    private final AuthorityService authorityService;

    private final BaseConfigService baseConfigService;

    private final RedisClient redisClient;

    /**
     *
     * @param bpEnterpriseInfoService
     * @param licenceModuleAuthService
     * @param baseModuleMenuMappingService
     * @param authorityService
     * @param baseConfigService
     * @param redisClient
     */
    public LicenceServiceImpl(BpEnterpriseInfoService bpEnterpriseInfoService,
                             LicenceModuleAuthService licenceModuleAuthService,
                             BaseModuleMenuMappingService baseModuleMenuMappingService,
                             AuthorityService authorityService,
                             BaseConfigService baseConfigService,
                             RedisClient redisClient){
        this.bpEnterpriseInfoService = bpEnterpriseInfoService;
        this.licenceModuleAuthService = licenceModuleAuthService;
        this.baseModuleMenuMappingService = baseModuleMenuMappingService;
        this.authorityService = authorityService;
        this.baseConfigService = baseConfigService;
        this.redisClient = redisClient;

        LicenceService.BASE_MODULE_MENU_MAPPING_CACHE.schedulePrune(5 * 1_000);
    }

    @Override
    public IPage<LicencePageDto> listPage(Integer page, Integer size) {
        LambdaQueryWrapper<Licence> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(BaseTenantEntity::getTenentId, Context.getCurrentTenantId());
        lambdaQueryWrapper.eq(BaseEnterpriseEntity::getOrgCode, Context.getCompanyCode());
        lambdaQueryWrapper.eq(BaseEnterpriseEntity::getDeleteFlag, DeleteFlag.EFFECTIVE.getCode());

        lambdaQueryWrapper.orderByDesc(Licence::getUpdateTime);
        return this.page(new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(page, size), lambdaQueryWrapper).convert(licence -> {
            LicencePageDto pageDto = new LicencePageDto();
            pageDto.setModuleName(licence.getModuleName());
            pageDto.setUpdateTime(DateUtil.format(licence.getUpdateTime(), "yyyy-MM-dd HH:mm"));
            return pageDto;
        });
    }

    @Override
    public Licence getByLicenceCode(Long tenentId, String orgCode, String licence) {
        LambdaQueryWrapper<Licence> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(BaseTenantEntity::getTenentId, tenentId);
        lambdaQueryWrapper.eq(BaseEnterpriseEntity::getOrgCode, orgCode);
        lambdaQueryWrapper.eq(BaseEnterpriseEntity::getDeleteFlag, DeleteFlag.EFFECTIVE.getCode());

        lambdaQueryWrapper.eq(Licence::getLicenceCode, licence);
        Licence oldLicence = this.getOne(lambdaQueryWrapper);
        return oldLicence;
    }

    @Override
    public IPage<LicenceModuleAuthPageDto> listModuleAuthPage(Integer page, Integer size) {
        LambdaQueryWrapper<LicenceModuleAuth> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(BaseTenantEntity::getTenentId, Context.getCurrentTenantId());
        lambdaQueryWrapper.eq(BaseEnterpriseEntity::getOrgCode, Context.getCompanyCode());

        lambdaQueryWrapper.orderByDesc(LicenceModuleAuth::getUpdateTime);
        DateTime now = DateUtil.beginOfDay(new Date());
        return this.licenceModuleAuthService.page(new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(page, size), lambdaQueryWrapper).convert(moduleAuth -> {
            LicenceModuleAuthPageDto pageDto = new LicenceModuleAuthPageDto();
            pageDto.setUpdateTime(DateUtil.format(moduleAuth.getUpdateTime(), "yyyy-MM-dd HH:mm"));
            pageDto.setModuleName(moduleAuth.getModuleName());
            String startTimeDecryptStr = AESUtil.decrypt(moduleAuth.getStartTime(), StrUtil.sub(StrUtil.fillAfter(moduleAuth.getModuleCode(), 'w', 16), 0, 16));
            String endTimeDecryptStr = AESUtil.decrypt(moduleAuth.getEndTime(), StrUtil.sub(StrUtil.fillAfter(moduleAuth.getModuleCode(), 'w', 16), 0, 16));
            DateTime startTime = DateTime.of(Long.parseLong(startTimeDecryptStr));
            pageDto.setStartTime(startTime.toString("yyyy-MM-dd"));
            DateTime endTime = DateTime.of(Long.parseLong(endTimeDecryptStr));
            pageDto.setEndTime(endTime.toString("yyyy-MM-dd"));
            if (now.isBefore(startTime)) {
                pageDto.setState("待生效");
            } else if (now.isBeforeOrEquals(endTime)) {
                pageDto.setState("生效中");
            } else {
                pageDto.setState("已过期");
            }

            return pageDto;
        });
    }

    @Override
    public List<LicenceModuleAuthRemindDto> listRemindModule() {
        LambdaQueryWrapper<LicenceModuleAuth> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(BaseTenantEntity::getTenentId, Context.getCurrentTenantId());
        lambdaQueryWrapper.eq(BaseEnterpriseEntity::getOrgCode, Context.getCompanyCode());
        lambdaQueryWrapper.eq(BaseEnterpriseEntity::getDeleteFlag, DeleteFlag.EFFECTIVE.getCode());

        List<LicenceModuleAuth> licenceModuleAuthList = this.licenceModuleAuthService.list(lambdaQueryWrapper);
        if (CollectionUtil.isEmpty(licenceModuleAuthList)) {
            return Collections.emptyList();
        }
        DateTime now = DateUtil.beginOfDay(new Date());
        int defaultRemindDays = 7;
        BaseConfig buildIdConfig = baseConfigService.findConfig(Context.getCurrentTenantId(), Context.getCompanyCode(), BaseConfigCodes.LOCATE_LICENCE_REMIND_DAYS);
        if (buildIdConfig != null) {
            try {
                defaultRemindDays = Integer.parseInt(buildIdConfig.getValue());
            } catch (NumberFormatException e) {
                defaultRemindDays = 7;
            }
        }
        Map<String, Set<String>> resultMap = new HashMap<>();
        for (LicenceModuleAuth moduleAuth : licenceModuleAuthList) {
            String startTimeDecryptStr = AESUtil.decrypt(moduleAuth.getStartTime(), StrUtil.sub(StrUtil.fillAfter(moduleAuth.getModuleCode(), 'w', 16), 0, 16));
            String endTimeDecryptStr = AESUtil.decrypt(moduleAuth.getEndTime(), StrUtil.sub(StrUtil.fillAfter(moduleAuth.getModuleCode(), 'w', 16), 0, 16));
            DateTime startTime = DateTime.of(Long.parseLong(startTimeDecryptStr));
            DateTime endTime = DateTime.of(Long.parseLong(endTimeDecryptStr));

            // 过滤已经到期的模块和未到期的模块
            if (now.isAfter(endTime) || now.isBefore(startTime)) {
                continue;
            }
            // 过滤没有到期需要提醒的模块
            DateTime newEndTime = DateUtil.offsetDay(now, defaultRemindDays);
            if (!newEndTime.isAfter(endTime)) {
                continue;
            }

            String endTimeFormat = endTime.toString("yyyy-MM-dd");
            Set<String> moduleNameSet = resultMap.computeIfAbsent(endTimeFormat, key -> new HashSet<>());
            moduleNameSet.add(moduleAuth.getModuleName());
        }
        List<LicenceModuleAuthRemindDto> result = new ArrayList<>(resultMap.size());
        // 按时间排序遍历
        for (String key : new TreeSet<>(resultMap.keySet())) {
            LicenceModuleAuthRemindDto licenceModuleAuthRemindDto = new LicenceModuleAuthRemindDto();
            licenceModuleAuthRemindDto.setRemindDate(key);
            licenceModuleAuthRemindDto.setModuleNames(resultMap.get(key));

            result.add(licenceModuleAuthRemindDto);
        }

        return result;
    }

    @Override
    public void expired() {
        int stepSize = 100;
        long enterpriseInfoMaxId = 0L;

        while (true) {
            Page<BpEnterpriseInfo> enterpriseInfoPage = bpEnterpriseInfoService.findByIdGreaterThan(enterpriseInfoMaxId, stepSize);
            if (enterpriseInfoPage.isEmpty()) {
                break;
            }

            Iterator<BpEnterpriseInfo> iterator = enterpriseInfoPage.iterator();
            // 遍历每家企业
            while (iterator.hasNext()) {
                BpEnterpriseInfo enterpriseInfo = iterator.next();
                Long tenentId = enterpriseInfo.getTenentId();
                String orgCode = enterpriseInfo.getOrgCode();
                Long id = enterpriseInfo.getId();
                // 查询授权模块到期时间，剔除到期模块菜单，更新 redis
                Set<String> authModuleSet = this.refreshBlackMenu(tenentId, orgCode, true);

                if (authModuleSet.isEmpty()) {
                    List<ShiroUser> shiroUserList = authorityService.kickOutUser(shiroUser -> orgCode.equals(shiroUser.getCompanyCode()));
                    LOGGER.debug("公司：{}，踢掉在线用户：{}", orgCode, shiroUserList.size());
                }

                enterpriseInfoMaxId = enterpriseInfoMaxId < id ? id : enterpriseInfoMaxId;
            }
        };
    }

    @Override
    public Set<String> refreshBlackMenu(Long tenentId, String orgCode, boolean useCache) {
        DateTime now = DateUtil.beginOfDay(new Date());
        Set<String> blackMenuUrlSet = new HashSet<>();
        // 获取授权的列表
        List<LicenceModuleAuthDto> licenceModuleAuthDtoList = licenceModuleAuthService.listByTenentIdAngOrgCode(tenentId, orgCode);

        // 如果基础模块 url 没有在企业授权模块列表下，需要添加黑名单
        Set<String> authModuleCodeSet = new HashSet<>();
        // 未来时间授权的模块，key：模块编码，value：开始时间
        Map<String, String> futureAuthModuleMap = new HashMap<>();
        for (LicenceModuleAuthDto licenceModuleAuth : licenceModuleAuthDtoList) {
            // 获取授权的菜单
            DateTime startTime = licenceModuleAuth.getStartTime();
            DateTime endTime = licenceModuleAuth.getEndTime();
            if (now.isAfterOrEquals(startTime) && now.isBeforeOrEquals(endTime)) {
                authModuleCodeSet.add(licenceModuleAuth.getModuleCode());
            }else if (now.isBefore(startTime)) {
                futureAuthModuleMap.put(licenceModuleAuth.getModuleCode(), startTime.toDateStr());
            }
        }
        // 校验基础配置表是否有过人为修改
        boolean isValidMapping = true;
        String mappingCacheKey = "BASE_MAPPING";
        Map<String, List<BaseModuleMenuMapping>> moduleMenuGroupByModuleCodeMap = BASE_MODULE_MENU_MAPPING_CACHE.get(mappingCacheKey);
        if (moduleMenuGroupByModuleCodeMap == null || !useCache) {
            LOGGER.debug("{}过期{}", mappingCacheKey, DateUtil.date());
            try {
                List<BaseModuleMenuMapping> baseModuleMenuMappingList = baseModuleMenuMappingService.listValid();
                moduleMenuGroupByModuleCodeMap = baseModuleMenuMappingList.stream()
                        .collect(Collectors.groupingBy(BaseModuleMenuMapping::getModuleCode));
            } catch (LicenceException e) {
                isValidMapping = false;
                moduleMenuGroupByModuleCodeMap = new HashMap<>();
                LOGGER.error("配置获取失败：{}-{}", e.getCode(), e.getMsg());
            }
            BASE_MODULE_MENU_MAPPING_CACHE.put(mappingCacheKey, moduleMenuGroupByModuleCodeMap);
        }

        // 授权过的 url
        Set<String> authMenuUrlSet = new HashSet<>();
        for (Map.Entry<String, List<BaseModuleMenuMapping>> entry : moduleMenuGroupByModuleCodeMap.entrySet()) {
            if (authModuleCodeSet.contains(entry.getKey())) {
                for (BaseModuleMenuMapping baseModuleMenuMapping : entry.getValue()) {
                    authMenuUrlSet.add(baseModuleMenuMapping.getMenuUrl());
                }
            }
        }
        for (Map.Entry<String, List<BaseModuleMenuMapping>> entry : moduleMenuGroupByModuleCodeMap.entrySet()) {
            if (authModuleCodeSet.contains(entry.getKey())) {
                continue;
            }
            // 如果配置模块不在授权模块中，需要添加黑名单
            for (BaseModuleMenuMapping baseModuleMenuMapping : entry.getValue()) {
                // 同一个 url 在不同模块下，以已授权有效的模块为准
                if (authMenuUrlSet.contains(baseModuleMenuMapping.getMenuUrl())) {
                    continue;
                }
                blackMenuUrlSet.add(AESUtil.encrypt(baseModuleMenuMapping.getMenuUrl(), BaseConfigCodes.LICENCE_AES_KEY));
            }
        }

        String licenceMenuKey = String.format(BaseConfigCodes.LICENCE_BLACKLIST_REDIS_KEY, tenentId);
        LicenceCacheDto licenceCacheDto = new LicenceCacheDto();
        int licenceState = 0;
        String licenceStateMsg = null;
        do {
            if (!authModuleCodeSet.isEmpty()) {
                break;
            }
            if (!futureAuthModuleMap.isEmpty()) {
                licenceState = LicenceStateEnum.FUTURE.getValue();
                TreeSet<String> futureAuthModuleTreeSet = new TreeSet<>(futureAuthModuleMap.values());
                Iterator<String> futureAuthModuleTreeSetIterator = futureAuthModuleTreeSet.iterator();
                licenceStateMsg = "当前Licence授权时间从" + futureAuthModuleTreeSetIterator.next() + "开始，请届时登录或重新申请Licence!";
                break;
            }
            if (licenceModuleAuthDtoList.isEmpty()) {
                licenceState = LicenceStateEnum.INIT.getValue();
                break;
            }
            licenceState = LicenceStateEnum.OVERDUE.getValue();
        } while (false);

        if (!isValidMapping) {
            licenceState = LicenceStateEnum.LICENCE_ERROR_BASE_MODULE_MENU.getValue();
        }

        licenceCacheDto.setState(licenceState);
        licenceCacheDto.setStateMsg(licenceStateMsg);
        licenceCacheDto.setBlackUrlSet(blackMenuUrlSet);
        licenceCacheDto.setDigest(licenceCacheDto.buildDigest());

        redisClient.hput(licenceMenuKey, orgCode, JSON.toJSONString(licenceCacheDto));
        return authModuleCodeSet;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void analysis(String licence, LicenceAnalysisDto licenceAnalysisDto) throws LicenceException {
        Date now = new Date();
        DateTime beginOfNow = DateUtil.beginOfDay(now);
        Long startDate = licenceAnalysisDto.getStartDate();
        Long endDate = licenceAnalysisDto.getEndDate();
        Long tenentId = licenceAnalysisDto.getTenentId();
        String orgCode = licenceAnalysisDto.getOrgCode();
        String companyName = licenceAnalysisDto.getCompanyName();

        List<LicenceFilePermissionDto> permissionDtoList = licenceAnalysisDto.getPermissionDtoList();

        Set<String> licenceModuleCodeEncryptSet = new HashSet<>();
        Set<String> licenceModuleNameSet = new HashSet<>();
        for (LicenceFilePermissionDto licenceFilePermissionDto : permissionDtoList) {
            String moduleCodeEncrypt = AESUtil.encrypt(licenceFilePermissionDto.getName(), BaseConfigCodes.LICENCE_AES_KEY);
            licenceFilePermissionDto.setModuleCodeEncrypt(moduleCodeEncrypt);

            licenceModuleNameSet.add(licenceFilePermissionDto.getName());
            licenceModuleCodeEncryptSet.add(moduleCodeEncrypt);
        }

        // key：模块编码module code，value：模块授权对象
        Map<String, LicenceModuleAuth> oldModuleAuthMap = new HashMap<>();
        List<LicenceModuleAuth> oldModuleAuthList = licenceModuleAuthService.listByTenentIdAngOrgCodeAndModuleCodes(tenentId, orgCode, licenceModuleCodeEncryptSet);
        if (CollectionUtil.isNotEmpty(oldModuleAuthList)) {
            oldModuleAuthMap = oldModuleAuthList.stream().collect(Collectors.toMap(LicenceModuleAuth::getModuleCode, Function.identity()));
        }
        List<LicenceModuleAuth> insertList = new ArrayList<>(permissionDtoList.size() - oldModuleAuthMap.size());
        List<LicenceModuleAuth> updateList = new ArrayList<>(oldModuleAuthMap.size());
        for (LicenceFilePermissionDto licenceFilePermissionDto : permissionDtoList) {
            LicenceModuleAuth moduleAuth = new LicenceModuleAuth();
            moduleAuth.setOrgCode(orgCode);
            moduleAuth.setTenentId(tenentId);
            if (licenceAnalysisDto.isLocalLicence()) {
                startDate = beginOfNow.getTime();
                endDate = DateUtil.offsetMonth(beginOfNow, 3).getTime();
            }
            moduleAuth.setModuleCode(AESUtil.encrypt(licenceFilePermissionDto.getName(), BaseConfigCodes.LICENCE_AES_KEY));
            moduleAuth.setEndTime(AESUtil.encrypt(endDate.toString(), StrUtil.sub(StrUtil.fillAfter(moduleAuth.getModuleCode(), 'w', 16), 0, 16)));
            moduleAuth.setStartTime(AESUtil.encrypt(startDate.toString(), StrUtil.sub(StrUtil.fillAfter(moduleAuth.getModuleCode(), 'w', 16), 0, 16)));
            moduleAuth.setModuleName(licenceFilePermissionDto.getName());
            moduleAuth.setUpdateTime(now);
            LicenceModuleAuth oldModuleAuth = oldModuleAuthMap.get(licenceFilePermissionDto.getModuleCodeEncrypt());
            if (oldModuleAuth == null) {
                insertList.add(moduleAuth);
                continue;
            }
            moduleAuth.setId(oldModuleAuth.getId());
            updateList.add(moduleAuth);
        }

        for (LicenceModuleAuth licenceModuleAuth : updateList) {
            LambdaUpdateWrapper<LicenceModuleAuth> licenceModuleAuthLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
            licenceModuleAuthLambdaUpdateWrapper.eq(LicenceModuleAuth::getId, licenceModuleAuth.getId());
            licenceModuleAuthLambdaUpdateWrapper.eq(BaseTenantEntity::getTenentId, licenceModuleAuth.getTenentId());
            licenceModuleAuthLambdaUpdateWrapper.eq(BaseEnterpriseEntity::getOrgCode, licenceModuleAuth.getOrgCode());
            licenceModuleAuthService.update(licenceModuleAuth, licenceModuleAuthLambdaUpdateWrapper);
        }

        licenceModuleAuthService.saveBatch(insertList);

        // 插入 licence 记录表
        Licence licenceEntity = new Licence();
        licenceEntity.setLicenceCode(licence);
        licenceEntity.setModuleName(StrUtil.join("、", licenceModuleNameSet.toArray()));
        licenceEntity.setTenentId(tenentId);
        licenceEntity.setOrgCode(orgCode);
        this.save(licenceEntity);

        // 更新授权模块权限
        this.refreshBlackMenu(tenentId, orgCode, false);

        // 更新企业名称
        if (!licenceAnalysisDto.isLocalLicence()) {
            bpEnterpriseInfoService.updateName(tenentId, orgCode, companyName);
        }
    }

    @Override
    public LicenceCacheDto getLicenceCacheDto(Long tenentid, String orgCode) {
        //
        String licenceMenuKey = String.format(BaseConfigCodes.LICENCE_BLACKLIST_REDIS_KEY, tenentid);
        // 如果licence解析结果不存在，则认为信息丢失或被篡改，需要重新上传解析licence
        LicenceCacheDto licenceCacheDto = redisClient.hget(licenceMenuKey, orgCode, LicenceCacheDto.class);
        if (licenceCacheDto == null) {
            this.refreshBlackMenu(tenentid, orgCode, false);
            licenceCacheDto = redisClient.hget(licenceMenuKey, orgCode, LicenceCacheDto.class);
        }
        return licenceCacheDto;
    }

    @Override
    public List<BaseMenuFunction> filter(List<BaseMenuFunction> baseMenuFunctionList) {
        LicenceCacheDto licenceCacheDto = this.getLicenceCacheDto(Context.getCurrentTenantId(), Context.getCurrentOrgCode());
        Set<String> blackUrlSet = licenceCacheDto.getDecryptedBlackUrlSet();
        Set<Long> blackParentIdSet = new HashSet<>();
        baseMenuFunctionList.removeIf(baseMenuFunction -> {
            boolean isBlackUrl = blackUrlSet.contains(baseMenuFunction.getUrl());
            if (isBlackUrl) {
                blackParentIdSet.add(baseMenuFunction.getId());
            }
            return isBlackUrl;
        });
        // 处理菜单下按钮，如果菜单屏蔽了，下面按钮也需要屏蔽
        baseMenuFunctionList.removeIf(baseMenuFunction -> {
            BaseMenuFunction parent = baseMenuFunction.getParent();
            if (parent == null) {
                return false;
            }
            return blackParentIdSet.contains(parent.getId());
        });
        // 处理流程引擎特殊按钮屏蔽
        // 业务流程可以有添加按钮的模块code
        Set<String> hasProcessesAddModuleCodeSet = new HashSet<>();
        hasProcessesAddModuleCodeSet.add(AESUtil.encrypt("作业票与流程引擎系统", BaseConfigCodes.LICENCE_AES_KEY));
        List<LicenceModuleAuth> moduleAuthList = licenceModuleAuthService
                .listByTenentIdAngOrgCodeAndModuleCodes(Context.getCurrentTenantId(), Context.getCurrentOrgCode(),
                        hasProcessesAddModuleCodeSet);
        boolean hasProcessesAdd = false;
        DateTime now = DateUtil.beginOfDay(new Date());
        if (CollectionUtil.isNotEmpty(moduleAuthList)) {
            for (LicenceModuleAuth moduleAuth : moduleAuthList) {
                String startTimeDecryptStr = AESUtil.decrypt(moduleAuth.getStartTime(), StrUtil.sub(StrUtil.fillAfter(moduleAuth.getModuleCode(), 'w', 16), 0, 16));
                String endTimeDecryptStr = AESUtil.decrypt(moduleAuth.getEndTime(), StrUtil.sub(StrUtil.fillAfter(moduleAuth.getModuleCode(), 'w', 16), 0, 16));
                DateTime startTime = DateTime.of(Long.parseLong(startTimeDecryptStr));
                DateTime endTime = DateTime.of(Long.parseLong(endTimeDecryptStr));
                if (now.isAfterOrEquals(startTime) && now.isBeforeOrEquals(endTime)) {
                    hasProcessesAdd = true;
                    break;
                }
            }

        }
        // 如果没有授权流程引擎的模块，需要剔除新增按钮
        if (!hasProcessesAdd) {
            baseMenuFunctionList.removeIf(baseMenuFunction -> "workflow:add".equals(baseMenuFunction.getPerm()));
        }
        return baseMenuFunctionList;
    }

}
